// import { sendRequest } from './config';
import qs from 'query-string';
import { MyRequest, MyResponse } from './net';
import { copyNewRes } from './utils';
import { SendFun } from './send';

type RequestStatus = 'none' | 'loading' | 'success';

type TypeWaitCallback = (res: MyResponse<any>) => void;

function generateKey(request: MyRequest): string {
  const { method = 'get' } = request;
  return `${method}::${request.url}?${qs.stringify(request.params)}`;
}

export default class CachePool {
  // eslint-disable-next-line no-use-before-define
  private static instance: CachePool;

  private cache: Map<string, MyResponse<any>>;

  private statusMap: Map<string, RequestStatus>;

  private callbackMap: Map<string, TypeWaitCallback[]>;

  private sendFun: SendFun;

  private constructor(fun: SendFun) {
    this.cache = new Map();
    this.statusMap = new Map();
    this.callbackMap = new Map();
    this.sendFun = fun;
  }

  public static getInstance(fun: SendFun): CachePool {
    if (!CachePool.instance) {
      CachePool.instance = new CachePool(fun);
    }
    return CachePool.instance;
  }

  public getCache(requestOption: MyRequest): Promise<MyResponse<any>> {
    const key = generateKey(requestOption);

    const status = this.statusMap.get(key);

    if (status === 'success') {
      if (this.cache.has(key)) {
        return Promise.resolve(
          copyNewRes(this.cache.get(key) as MyResponse<any>)
        );
      }
      return Promise.reject(new Error(`cache::${key} is undefined`));
    }

    if (status === 'loading') {
      return new Promise((resolve) => {
        const arr = this.callbackMap.get(key);
        if (arr) {
          arr.push((res) => {
            resolve(res);
          });
        } else {
          this.callbackMap.set(key, [
            (res) => {
              resolve(res);
            },
          ]);
        }
      });
    }

    this.statusMap.set(key, 'loading');

    return this.sendFun({
      ...requestOption,
      needCache: false,
    }).then((res) => {
      if (res && res.success) {
        this.statusMap.set(key, 'success');
        this.cache.set(key, res);
      } else {
        this.statusMap.set(key, 'none');
      }
      if (this.callbackMap.has(key)) {
        // 触发等待队列里的等待函数
        const arr = this.callbackMap.get(key);
        if (arr) {
          arr.forEach((cb) => {
            cb(copyNewRes(res));
          });
        }
        // 执行完成后将等待队列设为空
        this.callbackMap.delete(key);
      }

      return copyNewRes(res);
    });
  }
}
